﻿using DataLayer.Interfaces;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DataLayer.Implementations
{
    internal class NHibernateModel : INHibernateModel
    {
        private ISessionFactory sessionFactory;
        private static INHibernateModel instance;

        private NHibernateModel()
        {
            Configuration config = new Configuration();
            sessionFactory = config.Configure().BuildSessionFactory();
        }

        public static INHibernateModel GetInstance()
        {
            if (instance == null)
            {
                instance = new NHibernateModel();
            }
            return instance;
        }

        public T GetById<T>(int id)
        {
            using (var session = sessionFactory.OpenSession())
            {
                T result = session.Get<T>(id);
                return result;
            }
        }

        public IList<T> GetAll<T>() where T : class
        {
            using (var session = sessionFactory.OpenSession())
            {
                IList<T> result = session.QueryOver<T>().List();
                return result;
            }
        }

        public IList<T> GetAll<T>(string orderBy, bool asc) where T : class
        {
            using (var session = sessionFactory.OpenSession())
            {
                Order order;
                if (asc)
                {
                    order = Order.Asc(orderBy);
                }
                else
                {
                    order = Order.Desc(orderBy);
                }
                IList<T> result = session.CreateCriteria(typeof(T))
                    .AddOrder(order)
                    .List<T>();
                return result;
            }
        }

        public IList<T> GetByCriterion<T>(List<ICriterion> criterion) where T : class
        {
            using (var session = sessionFactory.OpenSession())
            {
                var query = session.QueryOver<T>();
                foreach (var criteria in criterion)
                {
                    query.And(criteria);
                }
                IList<T> result = query.List();
                return result;
            }
        }

        public void InsertOrUpdate<T>(T target, int id) where T : class
        {
            using (var session = sessionFactory.OpenSession())
            {
                ITransaction transaction = session.BeginTransaction();
                try
                {
                    var res = GetById<T>(id);
                    if (res != null)
                        session.SaveOrUpdate(target);
                    else
                        session.Save(target);
                    transaction.Commit();
                }
                catch (Exception)
                {
                    transaction.Rollback();
                    throw;
                }
                finally
                {
                    transaction.Dispose();
                }
            }
        }

        public void Delete<T>(List<T> list) where T : class
        {
            using (var session = sessionFactory.OpenSession())
            {
                ITransaction transaction = session.BeginTransaction();
                try
                {
                    foreach (T t in list)
                    {
                        session.Delete(t);
                    }
                    transaction.Commit();
                }
                catch (Exception)
                {
                    transaction.Rollback();
                    throw;
                }
                finally
                {
                    transaction.Dispose();
                }
            }
        }

        public void InsertOrUpdate<T>(List<T> list) where T : class
        {
            using (var session = sessionFactory.OpenSession())
            {
                ITransaction transaction = session.BeginTransaction();
                try
                {
                    foreach (T t in list)
                    {
                        session.SaveOrUpdate(t);
                    }
                    transaction.Commit();
                }
                catch (Exception)
                {
                    transaction.Rollback();
                    throw;
                }
                finally
                {
                    transaction.Dispose();
                }
            }
        }

        public ISessionFactory GetSessionFactory()
        {
            return sessionFactory;
        }
    }
}
